package com.xbongbong.saas.analytical.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.ConfigConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasFormEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.FormExplainHelp;
import com.xbongbong.paas.help.subform.TransferSubFormHelper;
import com.xbongbong.paas.help.team.TeamUserHelp;
import com.xbongbong.paas.model.PaasAppModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.pojo.dto.AddBatchDTO;
import com.xbongbong.paas.pojo.dto.FormDataAddDTO;
import com.xbongbong.paas.pojo.dto.FormDataUpdateGetDTO;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.pojo.vo.PaasFormDataEsListVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.domain.entity.FormConfigEntity;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.ContractErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.PaymentErrorCodeEnum;
import com.xbongbong.pro.enums.fund.enums.ModelTypeEnum;
import com.xbongbong.pro.formdata.pojo.SaasNeedRedundantAttrPojo;
import com.xbongbong.saas.analytical.SaasValidateAnalyticalService;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.ContractProductEntity;
import com.xbongbong.saas.domain.entity.InstockProductEntity;
import com.xbongbong.saas.domain.entity.OutstockProductEntity;
import com.xbongbong.saas.domain.entity.ext.ContractEntityExt;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.saas.enums.FormConfigEnum;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.RefundEnum;
import com.xbongbong.saas.enums.dictionary.ContractStatusEnum;
import com.xbongbong.saas.enums.dictionary.ShipStatusEnum;
import com.xbongbong.saas.enums.product.ContractProductEnum;
import com.xbongbong.saas.enums.subform.ContractProductSubFormEnum;
import com.xbongbong.saas.help.AnalyticalHelp;
import com.xbongbong.saas.help.SaasDataInitHelp;
import com.xbongbong.saas.help.SaasOutstockHelp;
import com.xbongbong.saas.help.SaasProductHelp;
import com.xbongbong.saas.help.SaveFormatHelp;
import com.xbongbong.saas.model.ContractProductModel;
import com.xbongbong.saas.model.FormConfigModel;
import com.xbongbong.saas.model.FundSetModel;
import com.xbongbong.saas.model.InstockProductModel;
import com.xbongbong.saas.model.OutstockProductModel;
import com.xbongbong.saas.service.toolbox.help.FundMoneyHelp;
import com.xbongbong.saas.service.toolbox.help.FundSetHelp;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.sys.domain.entity.CompanyConfigEntity;
import com.xbongbong.sys.model.CompanyConfigModel;
import com.xbongbong.util.OutstockUtil;
import com.xbongbong.util.ProductUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getDoubleOrDefaultFromFormData;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * @author 吴峰
 * @date 2021/07/15 15:12
 */
@Service("ContractAnalyticalServiceImpl")
@Slf4j
public class ContractValidateAnalyticalServiceImpl implements SaasValidateAnalyticalService {
    @Resource
    private FormExplainHelp formExplainHelp;
    @Resource
    private FormConfigModel formConfigModel;
    @Resource
    private FundMoneyHelp fundMoneyHelp;
    @Resource
    private ContractProductModel contractProductModel;
    @Resource
    private EsHelper esHelper;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private PaymentSheetValidateAnalyticalServiceImpl paymentSheetAnalyticalService;
    @Resource
    private OutstockProductModel outstockProductModel;
    @Resource
    private PaasAppModel paasAppModel;
    @Resource
    private SaasProductHelp saasProductHelp;
    @Resource
    private PaymentValidateAnalyticalServiceImpl paymentAnalyticalService;
    @Resource
    private TransferSubFormHelper transferSubFormHelper;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private InstockProductModel instockProductModel;
    @Resource
    private FundSetModel fundSetModel;
    @Resource
    private SaasOutstockHelp saasOutstockHelp;
    @Resource
    private FundSetHelp fundSetHelp;
    @Resource
    private AnalyticalHelp analyticalHelp;
    @Resource
    private TeamUserHelp teamUserHelp;

    @Override
    public List<Integer> type() {
        return Arrays.asList(XbbRefTypeEnum.CONTRACT.getCode());
    }

    @Override
    public void analyticalData(ValidateDataDTO validateDataDTO) throws XbbException {
        beforeSave(validateDataDTO);
        teamUserHelp.beforeVerifyUserTeam(validateDataDTO);
    }

    public void beforeSave(ValidateDataDTO validateDataDTO) throws XbbException {
        JSONObject data = validateDataDTO.getData();
        Long contractId = validateDataDTO.getDataId();
        Long taskId = validateDataDTO.getTaskId();
        String corpid = validateDataDTO.getCorpid();
        Long formId = validateDataDTO.getFormId();
        Integer distributorMark = validateDataDTO.getDistributorMark();
        Map<String, FieldAttrEntity> explainMap = validateDataDTO.getExplainMap();
        SaveFormatHelp.formatLinkBusiness4Save(data, ContractEnum.LINK_CUSTOMER.getAttr(), ContractEnum.LINK_CUSTOMER_HIDE.getAttr(), ContractEnum.LINK_CUSTOMER.getFieldType());
        SaveFormatHelp.formatLinkBusiness4Save(data, ContractEnum.LINK_CONTACT.getAttr(), ContractEnum.LINK_CONTACT_HIDE.getAttr(), ContractEnum.LINK_CONTACT.getFieldType());
        SaveFormatHelp.formatLinkBusiness4Save(data, ContractEnum.LINK_OPPORTUNITY.getAttr(), ContractEnum.LINK_OPPORTUNITY_HIDE.getAttr(), ContractEnum.LINK_OPPORTUNITY.getFieldType());
        SaveFormatHelp.formatLinkBusiness4Save(data, ContractEnum.LINK_PARTNER_CUSTOMER.getAttr(), ContractEnum.LINK_PARTNER_CUSTOMER_HIDE.getAttr(), ContractEnum.LINK_PARTNER_CUSTOMER.getFieldType());
        SaveFormatHelp.formatLinkBusiness4Save(data, ContractEnum.QUOTATION_ID.getAttr(), ContractEnum.QUOTATION_NAME.getAttr(), ContractEnum.QUOTATION_ID.getFieldType());
        boolean isNew = analyticalHelp.isNew(contractId, validateDataDTO.getTaskId(), corpid);
        Double amount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.AMOUNT.getAttr(), 0D);
        Double paymentTotalAmount = 0.0D;
        // 无签订人导入
        if (Objects.equals(validateDataDTO.getNoSignImport(), 1)) {
            //这时候会多一个离职，要去掉
            String signName = data.getString(ContractEnum.SIGN_NAME.getAttr());
            if (StringUtil.isNotEmpty(signName)) {
                signName = signName.replaceAll(StringConstant.LEAVE_MARK, "");
                data.put(ContractEnum.SIGN_NAME.getAttr(), data.getString(ContractEnum.SIGN_NAME.getAttr()));
            }
            SaveFormatHelp.formatLinkBusiness4Save(data, ContractEnum.SIGN_PERSON.getAttr(), ContractEnum.SIGN_NAME.getAttr(), FieldTypeEnum.LINK_BUSINESS_SINGLE.getType());

        }

        //导入时整单折扣string转成Double
        if (Objects.equals(validateDataDTO.getIsImport(), 1)) {
            if (!Objects.isNull(data.get(ContractEnum.DISCOUNT.getAttr()))) {
                Object discount = data.get(ContractEnum.DISCOUNT.getAttr());
                Double discountNum = Double.parseDouble(Objects.toString(discount, ""));
                data.put(ContractEnum.DISCOUNT.getAttr(), discountNum);
            }
        }

        if (isNew) {
            // 是否归档，1归档，2未归档
            //data.put(ContractEnum.ARCHIVED.getAttr(), 2);
            data.put(ContractEnum.UN_FINISH_AMOUNT.getAttr(), amount);
            data.put(ContractEnum.UN_INVOICE_AMOUNT.getAttr(), amount);
            data.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.UNSHIPPED.getCode());
            if (Objects.isNull(data.get(ContractEnum.DISCOUNT.getAttr()))) {
                data.put(ContractEnum.DISCOUNT.getAttr(), 1);
            }
        }
        //  编辑情况下根据产品数量变化，更改合同状态
        JSONArray productList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, ContractEnum.PRODUCT.getAttr(), new JSONArray());
        SaasNeedRedundantAttrPojo pojo = new SaasNeedRedundantAttrPojo();

        // 新建合同的时候如果没有关联产品则置为已出库
        boolean allOutBound;
        Boolean productCanSee = true;
        if (isNew) {
            allOutBound = validateProductDemo(productList, corpid, isNew, contractId, validateDataDTO.getFormId(), data.getLong(ContractEnum.LINK_CUSTOMER.getAttr()));
        } else {
            productCanSee = formExplainHelp.attrCanSee(explainMap.get(ContractEnum.PRODUCT.getAttr()), contractId, validateDataDTO.getCreatorId(), validateDataDTO.getLoginUser(), corpid, XbbRefTypeEnum.CONTRACT.getCode());
            if (productCanSee) {
                // 关联产品字段启用，可见，及高级可见时才校验关联产品
                allOutBound = validateProductDemo(productList, corpid, isNew, contractId, validateDataDTO.getFormId(), data.getLong(ContractEnum.LINK_CUSTOMER.getAttr()));
            } else {
                // 不可见就取是否出库完毕原值
                Integer outBound = FastJsonHelper.getIntegerOrDefaultFromFormData(data, ContractEnum.ALL_OUT_BOUND.getAttr(), BasicConstant.ONE);
                allOutBound = outBound != 0;
            }
            //编辑和新建和编辑进审批时完全分开
            PaasFormDataEntity paasFormDataEntity = validateDataDTO.getPaasFormDataEntity();
            //编辑进审批时，已经走过一遍beforesave了，无需在设置发货状态值
            if (Objects.nonNull(paasFormDataEntity)) {
                JSONObject oldData = paasFormDataEntity.getData();
                String shipStatus = FastJsonHelper.getStringOrDefaultFromFormData(oldData, ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.UNSHIPPED.getCode());
                if (!Objects.equals(shipStatus, ShipStatusEnum.CLOSED.getCode())) {
                    if (productList.isEmpty()) {
                        data.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.UNSHIPPED.getCode());
                    } else if (allOutBound) {
                        data.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.SHIPPED.getCode());
                    } else if (!Objects.equals(shipStatus, ShipStatusEnum.DELIVERED.getCode())) {
                        data.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.UNSHIPPED.getCode());
                    }
                }
            }
        }
        data.put(ContractEnum.ALL_OUT_BOUND.getAttr(), allOutBound ? 1 : 0);
        // 是否开启缺货待生产显示
        if (isNew) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.CONTRACT_NEW_SHOW_PRODUCE.getAlias(), corpid);
            data.put(ContractEnum.SHOW_PRODUCE.getAttr(), companyConfigEntity != null && companyConfigEntity.getConfigValue().equals(StringConstant.POSITIVE_NUMBER) ? 1 : 0);
        }
        // 合同状态不能主动修改成预签约
        // 合同状态是否需要统计
        String contractStatus = FastJsonHelper.getStringFromFormData(data, ContractEnum.STATUS.getAttr());
        data.put(ContractEnum.IF_STATISTIC.getAttr(), !Objects.equals(contractStatus, ContractStatusEnum.TERMINATION.getCode()) ? 1 : 0);
        // 需要校验一下合同开票金额是否大于合同金额，如果是这种情况就需要给出提示
        double invoiceAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, ContractEnum.INVOICE_AMOUNT.getAttr(), 0d);
        if (invoiceAmount > amount) {
            throw new XbbException(ContractErrorCodeEnum.API_ERROR_203020);
        }

        /*boolean isUseJxc = paasAppService.validateAppEnable(null, XbbRefTypeEnum.JXC.getAlias(), SaasMarkEnum.SAAS.getCode(), corpid);
        if (!isUseJxc){
            data.put(ContractEnum.EXPRESS_NAME.getAttr(),"");
            data.put(ContractEnum.EXPRESS_NO.getAttr(),"");
            data.put(ContractEnum.EXPRESS_STATUS.getAttr(),"");
        }*/
        List<Long> paymentFlowStatusIdList = new ArrayList<>();
        JSONArray paymentArray = FastJsonHelper.getJsonArrFromFormData(data, ContractEnum.ADD_PAYMENT.getAttr());
        getFlowStatusIdList(validateDataDTO, paymentArray, paymentFlowStatusIdList);
        Long nowModelType = fundSetModel.getModelType(corpid, XbbRefTypeEnum.CRM);
        //只要是当前模式是非自建模式，其它模式下合同中带有应收款的时候需要提示
        if (!Objects.equals(nowModelType, ModelTypeEnum.PLAN.getCode())
                && Objects.nonNull(paymentArray) && paymentArray.size() > BasicConstant.ZERO) {
            throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208078);
        }
        boolean fromApproval = Objects.nonNull(validateDataDTO.getTaskId());
        if (paymentArray != null && !paymentArray.isEmpty()) {
            PaasFormEntity paymentFormEntity = paasFormModel.getByBusinessType4Distributor(XbbRefTypeEnum.PAYMENT.getCode(), corpid, distributorMark, null);
            FormDataAddDTO formDataAddDTO = new FormDataAddDTO();
            BeanUtil.copyProperties(validateDataDTO, formDataAddDTO);
            formDataAddDTO.setAppId(paymentFormEntity.getAppId());
            formDataAddDTO.setMenuId(paymentFormEntity.getMenuId());
            formDataAddDTO.setFormId(paymentFormEntity.getId());
//            formDataAddDTO.setIsNew(isNew);
            JSONObject paymentData = new JSONObject();
            JSONObject customerData = new JSONObject();
            customerData.put(StringConstant.SAAS_LINK_BUSINESS_ID, data.getString(ContractEnum.LINK_CUSTOMER.getAttr()));
            customerData.put(StringConstant.SAAS_LINK_BUSINESS_NAME, data.getString(ContractEnum.LINK_CUSTOMER_HIDE.getAttr()));
            JSONObject contractData = new JSONObject();
            contractData.put(StringConstant.SAAS_LINK_BUSINESS_ID, contractId == null ? "0" : contractId);
            contractData.put(StringConstant.SAAS_LINK_BUSINESS_NAME, validateDataDTO.getSerialNo());
            paymentData.put(PaymentEnum.CUSTOMER.getAttr(), Collections.singletonList(customerData));
            paymentData.put(PaymentEnum.CONTRACT.getAttr(), Collections.singletonList(contractData));
            paymentData.put(PaymentEnum.ADD_PAYMENT.getAttr(), paymentArray);
            formDataAddDTO.setDataList(paymentData);
            formDataAddDTO.getSaasNeedRedundantAttrPoJo().setParentBusinessType(validateDataDTO.getBusinessType());
            formDataAddDTO.getSaasNeedRedundantAttrPoJo().setParentAmount(data.getDouble(ContractEnum.AMOUNT.getAttr()));
            formDataAddDTO.getSaasNeedRedundantAttrPoJo().setLinkItemFormId(validateDataDTO.getFormId());
            formDataAddDTO.setIsTotalPaymentSheetAmount(false);
            formDataAddDTO.setBusinessType(XbbRefTypeEnum.PAYMENT.getCode());
            AddBatchDTO addBatchDTO = paymentAnalyticalService.beforeSaveBatch(formDataAddDTO, fromApproval);
            paymentTotalAmount = Arith.add(paymentTotalAmount, addBatchDTO.getAmount());
            data.put(ContractEnum.ADD_PAYMENT.getAttr(), paymentArray);
            pojo.setAddBatchDTO(addBatchDTO);
        }
        /**
         * 新增回款单和核销预收款
         */
        List<Long> paymentSheetFlowStatusIdList = new ArrayList<>();
        List<Long> writeOffPrepaymentFlowStatusIdList = new ArrayList<>();
        JSONArray paymentSheetArray = FastJsonHelper.getJsonArrFromFormData(data, ContractEnum.ADD_PAYMENT_SHEET.getAttr());
        JSONArray writeOffPrepaymentArray = FastJsonHelper.getJsonArrFromFormData(data, ContractEnum.ADD_WRITE_OFF_PREPAYMENT.getAttr());
        getFlowStatusIdList(validateDataDTO, paymentSheetArray, paymentSheetFlowStatusIdList);
        getFlowStatusIdList(validateDataDTO, writeOffPrepaymentArray, writeOffPrepaymentFlowStatusIdList);
        if (CollectionsUtil.isNotEmpty(paymentSheetArray) || CollectionsUtil.isNotEmpty(writeOffPrepaymentArray)) {
            //由于回款单跟核销预收款的表单是一致的，所以还是用回款单的表单
            PaasFormEntity paymentSheetFormEntity = paasFormModel.getByBusinessType4Distributor(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), corpid, distributorMark, null);
            FormDataAddDTO paymentSheetAddDTO = new FormDataAddDTO();
            BeanUtil.copyProperties(validateDataDTO, paymentSheetAddDTO);
            paymentSheetAddDTO.setAppId(paymentSheetFormEntity.getAppId());
            paymentSheetAddDTO.setMenuId(paymentSheetFormEntity.getMenuId());
            paymentSheetAddDTO.setFormId(paymentSheetFormEntity.getId());
//            paymentSheetAddDTO.setIsNew(isNew);
            JSONObject paymentSheetData = new JSONObject();
            JSONObject customerData = new JSONObject();
            customerData.put(StringConstant.SAAS_LINK_BUSINESS_ID, data.getString(ContractEnum.LINK_CUSTOMER.getAttr()));
            customerData.put(StringConstant.SAAS_LINK_BUSINESS_NAME, data.getString(ContractEnum.LINK_CUSTOMER_HIDE.getAttr()));
            JSONObject contractData = new JSONObject();
            contractData.put(StringConstant.SAAS_LINK_BUSINESS_ID, contractId == null ? "0" : contractId);
            contractData.put(StringConstant.SAAS_LINK_BUSINESS_NAME, validateDataDTO.getSerialNo());
            paymentSheetData.put(PaymentSheetEnum.CUSTOMER_ID.getAttr(), Collections.singletonList(customerData));
            paymentSheetData.put(PaymentSheetEnum.CONTRACT.getAttr(), Collections.singletonList(contractData));
            if (CollectionsUtil.isNotEmpty(paymentSheetArray)) {
                paymentSheetData.put("addPaymentSheet", paymentSheetArray);
                paymentSheetAddDTO.setDataList(paymentSheetData);
                paymentSheetAddDTO.getSaasNeedRedundantAttrPoJo().setParentBusinessType(validateDataDTO.getBusinessType());
                paymentSheetAddDTO.getSaasNeedRedundantAttrPoJo().setParentAmount(data.getDouble(ContractEnum.AMOUNT.getAttr()));
                paymentSheetAddDTO.getSaasNeedRedundantAttrPoJo().setLinkItemFormId(validateDataDTO.getFormId());
                paymentSheetAddDTO.setBusinessType(XbbRefTypeEnum.PAYMENT_SHEET.getCode());
                AddBatchDTO addPaymentSheetBatchDTO = paymentSheetAnalyticalService.beforeSaveBatch(paymentSheetAddDTO, PaymentSheetTypeEnum.WRITE_OFF, data.getLongValue(ContractEnum.LINK_CUSTOMER.getAttr()), fromApproval);
                paymentTotalAmount = Arith.add(paymentTotalAmount, addPaymentSheetBatchDTO.getAmount());
                data.put(ContractEnum.ADD_PAYMENT_SHEET.getAttr(), paymentSheetArray);
                pojo.setAddPaymentSheetBatchDTO(addPaymentSheetBatchDTO);
            }
            if (CollectionsUtil.isNotEmpty(writeOffPrepaymentArray)) {
                paymentSheetData.put("addWriteOffPaymentSheet", writeOffPrepaymentArray);
                paymentSheetAddDTO.setDataList(paymentSheetData);
                paymentSheetAddDTO.getSaasNeedRedundantAttrPoJo().setParentBusinessType(validateDataDTO.getBusinessType());
                paymentSheetAddDTO.getSaasNeedRedundantAttrPoJo().setParentAmount(data.getDouble(ContractEnum.AMOUNT.getAttr()));
                paymentSheetAddDTO.getSaasNeedRedundantAttrPoJo().setLinkItemFormId(validateDataDTO.getFormId());
                paymentSheetAddDTO.setBusinessType(XbbRefTypeEnum.WRITE_OFF_PREPAYMENT.getCode());
                AddBatchDTO addWriteOffPrepaymentBatchDTO = paymentSheetAnalyticalService.beforeSaveBatch(paymentSheetAddDTO, PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT, data.getLongValue(ContractEnum.LINK_CUSTOMER.getAttr()), fromApproval);
                paymentTotalAmount = Arith.add(paymentTotalAmount, addWriteOffPrepaymentBatchDTO.getAmount());
                data.put(ContractEnum.ADD_WRITE_OFF_PREPAYMENT.getAttr(), writeOffPrepaymentArray);
                pojo.setAddWriteOffPrepaymentBatchDTO(addWriteOffPrepaymentBatchDTO);
            }
        }
        //查出这条合同相关的坏账与红冲金额
        if (!isNew) {
            FormDataUpdateGetDTO formDataUpdateGetDTO = new FormDataUpdateGetDTO();
            BeanUtil.copyProperties(validateDataDTO, formDataUpdateGetDTO);
            paymentTotalAmount = fundMoneyHelp.getImportCoverCalAmount(paymentTotalAmount, formDataUpdateGetDTO, explainMap,
                    XbbRefTypeEnum.CONTRACT.getCode(), validateDataDTO.getIsImport(), paymentFlowStatusIdList, paymentSheetFlowStatusIdList, writeOffPrepaymentFlowStatusIdList);
        }
        //允许误差小于等于ConfigConstant.amountAccuracy
        if (Arith.sub(paymentTotalAmount, amount) > ConfigConstant.amountAccuracy) {
            log.error("corpid:{},paymentTotalAmount:{},amount:{}",corpid,paymentTotalAmount,amount);
            throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208037);
        }
        if (!Objects.equals(validateDataDTO.getIsImport(), 1)) {
            FormConfigEntity formConfigEntity = formConfigModel.getEntityByFormId(validateDataDTO.getCorpid(), validateDataDTO.getFormId(), FormConfigEnum.CONTRACT_AMOUNT_EQUAL_REPAYMENT_PLAN.getConfig());
            boolean isContractAmountEqualPayment = Objects.equals(FormConfigEnum.CONTRACT_AMOUNT_EQUAL_REPAYMENT_PLAN.getConfigValue(), StringConstant.POSITIVE_NUMBER);
            if (Objects.nonNull(formConfigEntity)) {
                if (Objects.equals(formConfigEntity.getConfigValue(), StringConstant.POSITIVE_NUMBER)) {
                    isContractAmountEqualPayment = true;
                } else {
                    isContractAmountEqualPayment = false;
                }
            }
            if (isContractAmountEqualPayment) {
                if (!Objects.equals(paymentTotalAmount, amount)) {
                    throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208003);
                }
            }
        }
        if (isNew) {
            if (!data.containsKey(ContractEnum.OUTSTOCK_DISCOUNT_AMOUNT.getAttr())) {
                data.put(ContractEnum.OUTSTOCK_DISCOUNT_AMOUNT.getAttr(), BigDecimal.ZERO);
            }
            if (!data.containsKey(ContractEnum.OUTSTOCK_OTHER_EXPENSE.getAttr())) {
                data.put(ContractEnum.OUTSTOCK_OTHER_EXPENSE.getAttr(), BigDecimal.ZERO);
            }
        }
        // 校验合同金额和已出库金额
        if (!isNew) {
            PaasFormDataEsListVO contractOutstockListVO = saasOutstockHelp.getOutstockListFromEs(corpid, contractId, OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode(), distributorMark);
            // 只有出库记录才进行以下逻辑
            if(CollectionUtils.isNotEmpty(contractOutstockListVO.getPaasFormDataESList())){
                if(data.containsKey(ContractEnum.DISCOUNT_AMOUNT.getAttr()) && data.containsKey(ContractEnum.OUTSTOCK_DISCOUNT_AMOUNT.getAttr())){
                    BigDecimal discountAmount = data.getBigDecimal(ContractEnum.DISCOUNT_AMOUNT.getAttr());
                    BigDecimal outstockDiscountAmount = data.getBigDecimal(ContractEnum.OUTSTOCK_DISCOUNT_AMOUNT.getAttr());
                    if (Objects.nonNull(discountAmount) && Objects.nonNull(outstockDiscountAmount) && outstockDiscountAmount.compareTo(discountAmount) > 0) {
                        throw new XbbException(ContractErrorCodeEnum.API_ERROR_203023.getCode(), ContractErrorCodeEnum.API_ERROR_203023.getMsg());
                    }
                }
                if(data.containsKey(ContractEnum.OTHER_EXPENSE.getAttr()) && data.containsKey(ContractEnum.OUTSTOCK_OTHER_EXPENSE.getAttr())){
                    BigDecimal otherExpense = data.getBigDecimal(ContractEnum.OTHER_EXPENSE.getAttr());
                    BigDecimal outstockOtherExpense = data.getBigDecimal(ContractEnum.OUTSTOCK_OTHER_EXPENSE.getAttr());
                    if (Objects.nonNull(otherExpense) && Objects.nonNull(outstockOtherExpense) && outstockOtherExpense.compareTo(otherExpense) > 0) {
                        throw new XbbException(ContractErrorCodeEnum.API_ERROR_203024.getCode(), ContractErrorCodeEnum.API_ERROR_203024.getMsg());
                    }
                }
            }
            // 当按出库产生应收时,校验合同金额和已出库金额
            Long modelType = fundSetModel.getModelType(corpid,XbbRefTypeEnum.CRM);
            if(Objects.equals(modelType, ModelTypeEnum.OUTSTOCK.getCode())) {
                double contractOutstockTotalMoney = OutstockUtil.getContractOutstockTotalMoney(data, contractId, contractOutstockListVO);
                // 和已出库金额比较
                if (Arith.compare(contractOutstockTotalMoney, amount) > 0) {
                    throw new XbbException(ContractErrorCodeEnum.API_ERROR_203022.getCode(), ContractErrorCodeEnum.API_ERROR_203022.getMsg());
                }
            }
        }
        validateDataDTO.setSaasNeedRedundantAttrPoJo(pojo);
        // 各种利润信息在前面先算
//        if (isNew || productCanSee){
        calculateContractCost(validateDataDTO);
//        }

        for (Map.Entry<String, Object> entry : SaasDataInitHelp.initContract().entrySet()) {
            SaasDataInitHelp.antDataSet(data, entry.getKey(), entry.getValue());
        }
        Map<String, Boolean> attrCanSeeMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        attrCanSeeMap.put(ContractEnum.PRODUCT.getAttr(), productCanSee);
        validateDataDTO.getSaasNeedRedundantAttrPoJo().setAttrCanSeeMap(attrCanSeeMap);

        //这里还需要处理导入的应收账期，如果isImport为1就是导入，导入默认去获取默认账期
        if (Objects.equals(validateDataDTO.getIsImport(), 1)) {
            String period = fundSetHelp.getImportDefaultPeriod(corpid, formId, data);
            data.put(ContractEnum.RECEIVABLE_PERIOD.getAttr(), period);
            validateDataDTO.setData(data);
        } else {
            //处理应收账期字段,应收账期字段保存成json字符串
            JSONObject jsonObject = FastJsonHelper.getJsonObjectOrDefaultFromFormData(data, ContractEnum.RECEIVABLE_PERIOD.getAttr(), null);
            //合同订单，非手动创建应收的模式下，应收账期为空，并且应收账期不可见，设置默认值
            //当jsonObject为空时，代表应收账期字段是不可见的
            boolean flag = Objects.equals(validateDataDTO.getBusinessType(), XbbRefTypeEnum.CONTRACT.getCode());
            if (Objects.isNull(jsonObject) && flag){
                //获得当前数据创建的模式
                boolean periodFlag = !Objects.equals(ModelTypeEnum.PLAN.getCode(), nowModelType);
                if (periodFlag){
                    //获取关联的默认账期
                    String linkPeriodValue = fundSetHelp.getDefaultFormatPeriod(corpid, formId, data);
                    //获得关联应收帐期得attr
                    if (Objects.nonNull(linkPeriodValue)){
                        data.put(ContractEnum.RECEIVABLE_PERIOD.getAttr(), linkPeriodValue);
                    }
                }
            }else{
                String receivablePeriodStr = Objects.isNull(jsonObject) ? null : JSON.toJSONString(jsonObject);
                data.put(ContractEnum.RECEIVABLE_PERIOD.getAttr(), receivablePeriodStr);
            }
            validateDataDTO.setData(data);
        }
    }

    /**
     * 获取合同审批时回款计划、回款单、预收款的id
     *
     * @param jsonArray
     * @return
     */
    private void getFlowStatusIdList(ValidateDataDTO validateDataDTO, JSONArray jsonArray, List<Long> flowStatusIdList) {
        Long taskId = validateDataDTO.getTaskId();
        if (Objects.nonNull(taskId) && !taskId.equals(BasicConstant.ZERO_LONG)) {
            //代表是审批中
            for (int i = BasicConstant.ZERO; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                if (Objects.nonNull(jsonObject)) {
                    Object idObj = jsonObject.get(BasicConstant.ID);
                    if (Objects.nonNull(idObj)) {
                        flowStatusIdList.add(Long.parseLong(String.valueOf(idObj)));
                    }
                }
            }
        }
    }

    /**
     * beforeSave中的计算成本
     * @param validateDataDTO
     * @throws XbbException
     */
    public void calculateContractCost(ValidateDataDTO validateDataDTO) throws XbbException {
        Long contractId = validateDataDTO.getDataId();
        String corpid = validateDataDTO.getCorpid();
        boolean isNew = (contractId == null || Objects.equals(contractId, 0L));
        JSONObject contractData = validateDataDTO.getData();
        JSONArray productList;
        try {
            productList = JSONArray.parseArray(validateDataDTO.getData().getJSONArray(ContractEnum.PRODUCT.getAttr()).toJSONString());
        } catch (Exception e) {
            productList = new JSONArray();
        }
        validateDataDTO.getData().put(ContractEnum.PRODUCT.getAttr(), productList);
        double estimateCost = 0D;
        double realCost = 0D;
        if (isNew) {
            if (productList != null) {
                for (Object object : productList) {
                    JSONObject product = (JSONObject) object;
                    Double productCost = getDoubleOrDefaultFromFormData(product, ContractEnum.ESTIMATE_COST.getSaasAttr(), 0D);
                    Double productNum = getDoubleOrDefaultFromFormData(product, ContractProductEnum.NUM.getAttr(), 0D);
                    estimateCost = Arith.add(Arith.mul(productCost, productNum), estimateCost);
                }
            }
        } else {
            if (productList != null) {
                List<ContractProductEntity> contractProductList = contractProductModel.getProductsByConId(contractId, corpid);
                Map<Long, ContractProductEntity> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                contractProductList.forEach(item -> contractProductMap.put(item.getId(), item));
                Map<Long, String> avgCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<Long, Double> mergeNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (Object object : productList) {
                    JSONObject product = (JSONObject) object;
                    Long productId = FastJsonHelper.getLongFromFormData(product, ContractProductEnum.PRODUCT.getAttr());
                    Double productNum = getDoubleOrDefaultFromFormData(product, ContractProductEnum.NUM.getAttr(), 0D);
                    Long subProductId = FastJsonHelper.getLongFromFormData(product, BusinessConstant.PRODUCT_SUB_ID);
                    Double productCost;
                    if (contractProductMap.containsKey(subProductId)) {
                        productCost = contractProductMap.get(subProductId).getCost();
                        product.put(ContractEnum.ESTIMATE_COST.getSaasAttr(), productCost);
                    } else {
                        productCost = getDoubleOrDefaultFromFormData(product, ContractEnum.ESTIMATE_COST.getSaasAttr(), 0D);
                    }
                    if (mergeNumMap.containsKey(productId)) {
                        mergeNumMap.put(productId, Arith.add(mergeNumMap.get(productId), productNum));
                    } else {
                        mergeNumMap.put(productId, productNum);
                    }
                    if (avgCostMap.containsKey(productId)) {
                        // map中已经有相同产品，需要计算平均值
                        String productInfo = avgCostMap.get(productId);
                        String[] split = productInfo.split(",");
                        Double preCost = StringUtil.toDouble((split[0]), 0D);
                        Double preNum = StringUtil.toDouble((split[1]), 0D);
                        Double nowCost = SaasProductHelp.calculateCost(preNum, preCost, productNum, productCost, 1);
                        double nowNum = Arith.add(preNum, productNum);
                        avgCostMap.put(productId, nowCost + "," + nowNum);
                    } else {
                        // 将产品id为key，合同产品平均价和数量拼接字符串为value存入map中
                        String productInfo = productCost + "," + productNum;
                        avgCostMap.put(productId, productInfo);
                    }
                }

                BoolQueryBuilder boolQueryBuilder = boolQuery();
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                // 出库类型：1采购退货出库，2销售出库，3盘亏出库，4调拨出库，5其他出库，6装配出库, 7工单出库
                boolQueryBuilder.filter(termQuery("data." + OutstockEnum.TYPE.getAttr(), OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
                boolQueryBuilder.filter(termQuery("data." + OutstockEnum.REF_ID.getAttr(), contractId));
                boolQueryBuilder.filter(termQuery("del", 0));
                boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
                List<String> fieldList = new ArrayList<>();
                fieldList.add(FieldTypeEnum.DATAID.getAlias());
                List<PaasFormDataEntityExt> outstockList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_OUTSTOCK, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
                List<Long> outstockIdIn = new ArrayList<>();
                for (PaasFormDataEntityExt outstockEsEntity : outstockList) {
                    outstockIdIn.add(outstockEsEntity.getDataId());
                }

                // 拿到所有的已出库产品
                List<OutstockProductEntity> outstockProductEntityList = new ArrayList<>();
                if (!outstockIdIn.isEmpty()) {
                    Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    param.put("corpid", corpid);
                    param.put("del", 0);
                    param.put("outstockIdIn", outstockIdIn);
                    outstockProductEntityList = outstockProductModel.findEntitys(param);
                }
                boolean isOld = false;
                realCost = 0D;
                for (OutstockProductEntity outstockProductEntity : outstockProductEntityList) {
                    Double productNum = outstockProductEntity.getProductNum();
                    productNum = productNum == null ? 0D : productNum;
                    Double cost = outstockProductEntity.getCost();
                    cost = cost == null ? 0D : cost;
                    realCost = Arith.add(realCost, Arith.mul(productNum, cost));
                    if (Objects.isNull(outstockProductEntity.getRefProductId())) {
                        isOld = true;
                    }
                }
                if (isOld) {
                    Map<Long, Double> outstockProductNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    for (OutstockProductEntity entity : outstockProductEntityList) {
                        Long productId = entity.getProductId();
                        if (outstockProductNumMap.containsKey(productId)) {
                            outstockProductNumMap.put(productId, Arith.add(outstockProductNumMap.get(productId), entity.getProductNum()));
                        } else {
                            outstockProductNumMap.put(productId, entity.getProductNum());
                        }
                    }
                    for (Map.Entry<Long, Double> entry : mergeNumMap.entrySet()) {
                        Long productId = entry.getKey();
                        Double num = entry.getValue();
                        if (!avgCostMap.containsKey(productId)) {
                            continue;
                        }
                        String[] productInfos = avgCostMap.get(productId).split(",");
                        if (outstockProductNumMap.containsKey(productId)) {
                            if (Arith.sub(num, outstockProductNumMap.get(productId)) > 0) {
                                estimateCost = Arith.add(estimateCost, Arith.mul(Arith.sub(num, outstockProductNumMap.get(productId)), StringUtil.toDouble((productInfos[0]), 0D)));
                            }
                        } else {
                            estimateCost = Arith.add(estimateCost, Arith.mul(num, StringUtil.toDouble((productInfos[0]), 0D)));
                        }
                    }
                } else {
                    for (Object object : productList) {
                        JSONObject product = (JSONObject) object;
                        Double productNum = getDoubleOrDefaultFromFormData(product, ContractProductEnum.NUM.getAttr(), 0D);
                        Long subProductId = FastJsonHelper.getLongFromFormData(product, BusinessConstant.PRODUCT_SUB_ID);
                        Double productCost = getDoubleOrDefaultFromFormData(product, ContractEnum.ESTIMATE_COST.getSaasAttr(), 0D);
                        if (contractProductMap.containsKey(subProductId)) {
                            ContractProductEntity contractProductEntity = contractProductMap.get(subProductId);
                            if (Arith.sub(productNum, contractProductEntity.getContractOutstockNum()) > 0) {
                                estimateCost = Arith.add(estimateCost, Arith.mul(contractProductEntity.getCost(), Arith.sub(productNum, contractProductEntity.getContractOutstockNum())));
                            }
                        } else {
                            estimateCost = Arith.add(estimateCost, Arith.mul(productNum, productCost));
                        }
                    }
                }
            }
        }

        // 计算退货退款的因素
        Boolean isJxcUse = paasAppModel.validateAppEnable(null, XbbRefTypeEnum.JXC.getAlias(), SaasMarkEnum.SAAS.getCode(), corpid);
        Map<Long, Double> contractIdAndCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> allContractCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        calculateContractRefundAmount(corpid, contractId, isJxcUse, contractIdAndCostMap, allContractCostMap);

        //2018/6/22 凤娇优惠不计入成本
        //Double surchargeAmount = calculateSurcharge(contractSurchargeList);
        Double surchargeAmount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.OTHER_EXPENSE.getAttr(), BasicConstant.ZERO_DOUBLE);

        // 合同成本 = 合同产品成本 - 退货产品成本（入库单的产品成本：新数据用入库单的成本，旧数据用出库单的平均成本）+ 其他费用
        Double contractCost;
        if (contractIdAndCostMap.containsKey(contractId)) {
            Double cost = contractIdAndCostMap.get(contractId) == null ? BasicConstant.ZERO_DOUBLE : contractIdAndCostMap.get(contractId);
            contractCost = Arith.sub(Arith.add(Arith.add(estimateCost, realCost), surchargeAmount), cost);
        } else {
            contractCost = Arith.add(Arith.add(estimateCost, realCost), surchargeAmount);
        }
        contractData.put(ContractEnum.ESTIMATE_COST.getAttr(), Arith.round(estimateCost, 2));
        contractData.put(ContractEnum.REAL_COST.getAttr(), Arith.round(realCost, 2));
        contractData.put(ContractEnum.CONTRACT_COST.getAttr(), Arith.round(contractCost, 2));

        Double amount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.AMOUNT.getAttr(), 0D);
        //合同毛利 = 合同金额 - 退货金额（关联计算）- 合同成本（同上）
        Double grossProfit;
        if (allContractCostMap.containsKey(contractId)) {
            Double cost = allContractCostMap.get(contractId) == null ? BasicConstant.ZERO_DOUBLE : allContractCostMap.get(contractId);
            grossProfit = Arith.sub(Arith.sub(amount, contractCost), cost);
        } else {
            grossProfit = Arith.sub(amount, contractCost);
        }
        contractData.put(ContractEnum.GROSS_PROFIT.getAttr(), Arith.round(grossProfit, 2));
        //合同毛利率
        if (Double.compare(amount, BasicConstant.ZERO_DOUBLE) != 0) {
            contractData.put(ContractEnum.GROSS_PROFIT_RATE.getAttr(), Arith.round(Arith.div(grossProfit, amount), 4));
        } else {
            contractData.put(ContractEnum.GROSS_PROFIT_RATE.getAttr(), BasicConstant.ZERO_DOUBLE);
        }
        Double finishAmount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
        Double badAmount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.BAD_AMOUNT.getAttr(), 0D);
        Double unAmount = amount - finishAmount - badAmount;
        Double unInvoiceMoney = getDoubleOrDefaultFromFormData(contractData, ContractEnum.INVOICE_AMOUNT.getAttr(), 0D);
        double cashProfit = Arith.sub(finishAmount, contractCost);
        contractData.put(ContractEnum.CASH_PROFIT.getAttr(), cashProfit);
        contractData.put(ContractEnum.UN_FINISH_AMOUNT.getAttr(), unAmount);
        double payPercent;
        //合同金额为0时候 收款比例为100%
        if (amount == 0) {
            payPercent = 1;
        } else {
            payPercent = Arith.round(Arith.div(finishAmount, amount), 4);
        }
        contractData.put(ContractEnum.PAY_PERCENT.getAttr(), payPercent);
        contractData.put(ContractEnum.UN_INVOICE_AMOUNT.getAttr(), amount - unInvoiceMoney);
        if (Double.compare(finishAmount, BasicConstant.ZERO_DOUBLE) != 0) {
            contractData.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), Arith.round(Arith.div(cashProfit, finishAmount), 4));
        } else {
            contractData.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), BasicConstant.ZERO_DOUBLE);
        }
    }

    /**
     * 计算出合同总计需要退货金额以及退货合同总成本
     * contractIdAndCostMap 合同与红冲合同退货产品成本对应map
     * allContractCostMap 查询出所有关联原合同的退货金额
     */
    public void calculateContractRefundAmount(String corpid, Long contractId, boolean isJxcUse,
                                              Map<Long, Double> contractIdAndCostMap, Map<Long, Double> allContractCostMap) throws XbbException {
        // 合同ID为空，合同还未创建，不计算下面的逻辑
        if (contractId == null) {
            return;
        }
        // 查询出所有关联原合同的退货金额(一对多)
        // 合同与红冲合同对应map
        Map<Long, List<Long>> contractIdAndRedContractMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

        // 合同与入库单对应map
        Map<Long, List<Long>> contractIdAndInstockIdMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);


        BoolQueryBuilder oldQueryBuilder = boolQuery();
        oldQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        oldQueryBuilder.filter(termsQuery(RefundEnum.getEsAttr4Keyword(RefundEnum.CONTRACT_ID), Arrays.asList(contractId)));
        oldQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        oldQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_REFUND.getType()));
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(oldQueryBuilder);
        PageRequest page = EsUtil.setPage(builder, 1, PaasConstant.ES_MAX_PAGE_SIZE);
        SearchRequest search = new SearchRequest(IndexTypeEnum.IDX_SAAS_REFUND.getIndex());
        search.source(builder);
        XbbAggregatedPage<PaasFormDataEntity> entities = xbbElasticsearchRestTemplate.queryForPages(page, search, PaasFormDataEntity.class);
        for (PaasFormDataEntity entity : entities.getContent()) {
            JSONObject data = entity.getData();
            Double returnCost = getDoubleOrDefaultFromFormData(data, RefundEnum.RETURN_AMOUNT.getAttr(), 0D);

            // 统计所有的退货金额
            if (allContractCostMap.containsKey(contractId)) {
                Double cost = allContractCostMap.get(contractId) == null ? 0D : allContractCostMap.get(contractId);
                returnCost = Arith.add(returnCost, cost);
                allContractCostMap.put(contractId, returnCost);
            } else {
                allContractCostMap.put(contractId, returnCost);
            }

            // 合同与红冲合同对应关系
            Long redConId = data.getLong(RefundEnum.RED_CONTRACT_ID.getAttr());
            if (contractIdAndRedContractMap.containsKey(contractId)) {
                List<Long> redConIdList = contractIdAndRedContractMap.get(contractId) == null ? new ArrayList<>() : contractIdAndRedContractMap.get(contractId);
                List<Long> idList = new ArrayList<>(redConIdList);
                idList.add(redConId);
                contractIdAndRedContractMap.put(contractId, idList);
            } else {
                contractIdAndRedContractMap.put(contractId, Arrays.asList(redConId));
            }

            // 合同与入库单对应关系
            JSONArray instockIdArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, RefundEnum.INSTOCK_ID.getAttr(), new JSONArray());
            if (!instockIdArr.isEmpty()) {
                List<Long> instocks = instockIdArr.toJavaList(Long.class);
                if (contractIdAndInstockIdMap.containsKey(contractId)) {
                    List<Long> instockList = contractIdAndInstockIdMap.get(contractId) == null ? new ArrayList<>() : contractIdAndInstockIdMap.get(contractId);
                    List<Long> idList = new ArrayList<>(instockList);
                    idList.addAll(instocks);
                    contractIdAndInstockIdMap.put(contractId, idList);
                } else {
                    contractIdAndInstockIdMap.put(contractId, instocks);
                }
            }
        }

        // 查询出所有关联原合同的出库单
        Map<Long, List<Long>> allContractOutMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // 所有对应的合同实体
        List<ContractEntityExt> contractList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> contractIdList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        BoolQueryBuilder entityQueryBuilder = boolQuery();
        entityQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        entityQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(), Arrays.asList(contractId)));
        entityQueryBuilder.filter(termQuery("del", 0));
        entityQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_CONTRACT.getType()));
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(entityQueryBuilder);
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 1, 1);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_CONTRACT.getIndex());
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<ContractEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, ContractEntityExt.class);
        for (ContractEntityExt entity : esEntities.getContent()) {
            contractList.add(entity);
            contractIdList.add(contractId);

            JSONObject data = entity.getData();
            List<Long> outstockIdList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, ContractEnum.OUTSTOCK_ID.getAttr(), new JSONArray()).toJavaList(Long.class);

            if (allContractOutMap.containsKey(contractId)) {
                List<Long> outIdList = allContractOutMap.get(contractId) == null ? new ArrayList<>() : allContractOutMap.get(contractId);
                List<Long> outIdIn = new ArrayList<>(outIdList);
                outIdIn.addAll(outstockIdList);
                allContractOutMap.put(contractId, outIdIn);
            } else {
                allContractOutMap.put(contractId, outstockIdList);
            }
        }

        Double jxc2UpdateInstockCost = BasicConstant.ZERO_DOUBLE;

        // 查询该合同对应所有入库单的产品
        List<Long> instockList = contractIdAndInstockIdMap.get(contractId) == null ? new ArrayList<>() : contractIdAndInstockIdMap.get(contractId);
        //入库单的产品id及其数量
        Map<Long, Double> instockProductIdAndNum = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (!instockList.isEmpty()) {
            Map<String, Object> instockProductParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            instockProductParam.put("corpid", corpid);
            instockProductParam.put("instockIdIn", instockList);
            instockProductParam.put("del", 0);
            List<InstockProductEntity> instockProductEntityList = instockProductModel.findEntitys(instockProductParam);
            for (InstockProductEntity instockPro : instockProductEntityList) {
                // 新数据计算，直接计算所有该合同所有入库单的成本
                Double productNum = instockPro.getProductNum() == null ? 0D : Math.abs(instockPro.getProductNum());
                Double productCost = instockPro.getCost() == null ? 0D : instockPro.getCost();
                jxc2UpdateInstockCost = Arith.add(jxc2UpdateInstockCost, Arith.mul(productNum, productCost));

                // 统计入库单每一件产品的数量
                Long productId = instockPro.getProductId();
                Double num = instockPro.getProductNum();
                if (instockProductIdAndNum.containsKey(productId)) {
                    Double inNum = instockProductIdAndNum.get(productId) == null ? BasicConstant.ZERO_DOUBLE : instockProductIdAndNum.get(productId);
                    Double countNum = Arith.add(inNum, num);
                    instockProductIdAndNum.put(productId, countNum);
                } else {
                    instockProductIdAndNum.put(productId, num);
                }
            }
        }

        // 查询所有的红冲合同
        Iterator entries = contractIdAndRedContractMap.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry entry = (Map.Entry) entries.next();
            Long key = (Long) entry.getKey();
            List<Long> redConIdList = contractIdAndRedContractMap.get(key);
            Double costAmount = BasicConstant.ZERO_DOUBLE;
            if (redConIdList.size() > 0) {
                Map<Long, Double> productIdAndNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<String, Object> redParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                redParam.put("corpid", corpid);
                redParam.put("contractIdIn", redConIdList);
                redParam.put("del", 0);
                // 拿到红冲合同的所有产品
                List<ContractProductEntity> contractProductEntityList = contractProductModel.findEntitys(redParam);
                //判断是否是新旧数据
                boolean sourceDoc = false;
                for (ContractProductEntity entity : contractProductEntityList) {
                    JSONObject data = entity.getData();
                    if (data.containsKey(SelectProductEnum.SOURCE_DOC.getAttr())) {
                        sourceDoc = true;
                        break;
                    }
                }
                // 新数据直接使用累加结果
                if (sourceDoc) {
                    contractIdAndCostMap.put(key, jxc2UpdateInstockCost);
                    break;
                }

                //-----------------------旧数据处理---------------------------
                // 退货产品ID以及对应的数量
                for (ContractProductEntity entity : contractProductEntityList) {
                    Long pageProductId = entity.getProductId();
                    Double productNum = entity.getProductNum() == null ? 0D : Math.abs(entity.getProductNum());
                    if (productIdAndNumMap.containsKey(pageProductId)) {
                        Double num = productIdAndNumMap.get(pageProductId) == null ? 0D : productIdAndNumMap.get(pageProductId);
                        productNum = Arith.add(productNum, num);
                    }
                    productIdAndNumMap.put(pageProductId, productNum);
                }

                List<Long> outstockIdList = allContractOutMap.get(key);
                if (outstockIdList != null && instockList != null && isJxcUse) {
                    Map<String, Object> outstockParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    outstockParam.put("corpid", corpid);
                    outstockParam.put("del", DelEnum.NORMAL.getDel());
                    outstockParam.put("outstockIdIn", outstockIdList);
                    List<OutstockProductEntity> outstockProductList = outstockProductModel.findEntitys(outstockParam);
                    Map<Long, OutstockProductEntity> outstockProductEntityMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    // 算出每个产品对应的成本
                    Map<Long, Double> productIdAndCost = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    // 所有的出库记录
                    for (OutstockProductEntity outstockProductEntity : outstockProductList) {
                        Long productId = outstockProductEntity.getProductId();
                        Double productNum = outstockProductEntity.getProductNum() == null ? 0D : outstockProductEntity.getProductNum();
                        Double productCost = outstockProductEntity.getCost() == null ? 0D : outstockProductEntity.getCost();
                        // 将所有记录的相同产品的平均成本求出
                        if (outstockProductEntityMap.containsKey(productId)) {
                            OutstockProductEntity oldOutstockProductEntity = outstockProductEntityMap.get(productId);
                            Double oldProductNum = oldOutstockProductEntity.getProductNum() == null ? 0D : oldOutstockProductEntity.getProductNum();
                            Double oldProductCost = oldOutstockProductEntity.getCost() == null ? 0D : oldOutstockProductEntity.getCost();
                            // 上一次的产品总成本
                            Double oldCost = Arith.mul(oldProductNum, oldProductCost);
                            // 这一次的产品总成本
                            Double newCost = Arith.mul(productNum, productCost);
                            // (上一次的产品总成本+这一次的产品总成本)/两次的总数量
                            productCost = Arith.div(Arith.add(oldCost, newCost), Arith.add(oldProductNum, productNum), 2);
                        }
                        outstockProductEntityMap.put(productId, outstockProductEntity);
                        productIdAndCost.put(productId, productCost);
                    }
                    // 遍历入库产品
                    Iterator iterator = instockProductIdAndNum.keySet().iterator();
                    while (iterator.hasNext()) {
                        Long id = Long.valueOf(iterator.next().toString());
                        Double productNum = instockProductIdAndNum.get(id);
                        if (productIdAndCost.containsKey(id)) {
                            costAmount = Arith.add(costAmount, Arith.mul(productNum, productIdAndCost.get(id)));
                        }
                    }
                } else if (!isJxcUse) {
                    // 不开启进销存是直接查询原合同关联产品表的成本*红冲合同产品表的产品数量(退货产品数量)
                    BoolQueryBuilder boolQueryBuilder = boolQuery();
                    boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                    boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
                    boolQueryBuilder.filter(termQuery(ContractProductSubFormEnum.getEsAttr(ContractProductSubFormEnum.TYPE), 201));
                    boolQueryBuilder.filter(termQuery(ContractProductSubFormEnum.getEsAttr(ContractProductSubFormEnum.CONTRACT_ID), key));
                    boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_CONTRACT_PRODUCT.getType()));
                    List<String> fieldList = Arrays.asList(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_ID), ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.CONTRACT_ID), ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_NUM), ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRICE), ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.COST));
                    List<PaasFormDataEntity> list = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT_PRODUCT, boolQueryBuilder, PaasFormDataEntity.class, fieldList);
                    if (list.size() > 0) {
                        List<ContractProductEntity> contractProductEntities = transferSubFormHelper.transferFormDataToContractProductList(list);
                        for (ContractProductEntity contractProductEntity : contractProductEntities) {
                            Long productId = contractProductEntity.getProductId();
                            Double cost = contractProductEntity.getCost() == null ? 0D : contractProductEntity.getCost();
                            if (productIdAndNumMap.containsKey(productId)) {
                                Double productCost = Arith.mul(cost, productIdAndNumMap.get(productId));
                                costAmount = Arith.add(costAmount, productCost);
                            }
                        }
                    }
                }
            }
            contractIdAndCostMap.put(key, costAmount);
        }
    }
    /**
     * 合同产品保存校验
     * @param productList
     * @param corpid
     * @param isNew
     * @param contractId
     * @return
     * @throws XbbException
     */
    public boolean validateProductDemo(JSONArray productList, String corpid, boolean isNew, Long contractId, Long formId, Long refId) throws XbbException {
        if (isNew && (Objects.isNull(productList) || productList.isEmpty())) {
            return true;
        }
        Set<Long> pageProductIds = new HashSet<>();
        for (Object obj : productList) {
            JSONObject json = (JSONObject) obj;
            Long productId = json.getLong(SelectProductEnum.PRODUCT.getAttr());
            pageProductIds.add(productId);
        }
        //判断产品合法
        Map<Long, String> pageProductMap = saasProductHelp.productExitContract(productList, corpid, new ArrayList<>(pageProductIds));

        boolean allbound = false;
        //允许合同出库产品和数量与关联产品不一致
        boolean isUseJxc = paasAppModel.validateAppEnable(null, XbbRefTypeEnum.JXC.getAlias(), SaasMarkEnum.SAAS.getCode(), corpid);
        if (!isNew && isUseJxc) {
            Map<String, Double> businessProductMap = saasProductHelp.getOutstockProduct(corpid, contractId, OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode(), pageProductMap);
            boolean hasApprovalData = businessProductMap.containsKey(BusinessConstant.HAS_APPROVAL_DATA);
            businessProductMap.remove(BusinessConstant.HAS_APPROVAL_DATA);
            boolean haveCorresponding = false;
            for (String s : businessProductMap.keySet()) {
                if (s.contains("_")) {
                    haveCorresponding = true;
                    break;
                }
            }
            boolean needMerge = !businessProductMap.isEmpty() && !haveCorresponding;
            Map<String, Double> productNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (Object obj : productList) {
                JSONObject json = (JSONObject) obj;
                Long productId = json.getLong(SelectProductEnum.PRODUCT.getAttr());
                Long productSubId = json.getLong(BusinessConstant.PRODUCT_SUB_ID);
                String key;
                if (needMerge) {
                    key = productId.toString();
                } else {
                    key = productId + "_" + productSubId;
                }
                Double productNum = json.getDouble(SelectProductEnum.NUM.getAttr());
                Double num = productNumMap.getOrDefault(key, 0D);
                num = Arith.add(num, productNum);
                productNumMap.put(key, num);
            }
            boolean isOverOutstockOpen = formConfigModel.hasBeenEnable(corpid, FormConfigEnum.CONTRACT_ALLOW_OUTSTOCK_PRODUCT_NUM_LARGER_SOLD.getConfig(), StringConstant.POSITIVE_NUMBER, formId);
            allbound = ProductUtil.upstreamDocumentsProductNumLegalCheck(isOverOutstockOpen, businessProductMap, productNumMap, pageProductMap, I18nMessageUtil.getMessage(I18nStringConstant.LESS_SALE_STOCK_NUM));
            //bug23482:还需要判断一次编辑时是否添加删除了产品
//            if (productList.size()>businessProductMap.size()){
//                allbound = false;
//            }
            allbound = allbound && !hasApprovalData;
        }
        return allbound;
    }
}
